/******************************************************************************
 * \brief	私有通信协议服务器端和客户端共用的函数接口
 * \details	与scp_server.c和scp_client.c中的接口一起实现通信模块的调用；
 *			SCP的含义是Self Communication Protocol，自己的通信协议；
 *			SCP协议处于传输层，传输层的数据包名为segment
 * \note	File format: UTF-8，中文编码：UTF-8
 * \author	将狼才鲸
 * \date	2023-03-23
 ******************************************************************************/

/*********************************** 头文件 ***********************************/
#include "scp.h"
#include "scp_client.h"
//#include "scp_server.h"
#include <string.h>	/* memcpy */

/********************************** 全局变量 **********************************/
/* 可以建立多个通信连接，这里只保留一个默认的连接句柄，其它的连接句柄需要调用者自行保存 */
scp_handle scp = NULL;	/* 默认通信句柄，全局变量，在别的模块中直接赋值 */

/********************************** 私有函数 **********************************/
/**
 * \brief	一次通信连接前申请通信句柄结构体空间
 * \details	该结构体不通过定义的方式开辟空间，而是从系统缓存中获取
 * \param	priv_len:	额外需要的空间（当前未使用）
 */
static scp_t *scp_new(int priv_len)
{
	int i;
	scp_t *pscp = NULL;	/* 通信句柄 */
	qbuf_t *qbuf;		/* 存放通信句柄结构体的缓存 */
	irq_flag lock;

	qbuf = qbuf_alloc(QBUF_GROUP_TMPBUF);	/* 申请缓存 */
	if (!qbuf)
		return NULL;

	irq_disable(lock);

	pscp = (scp_t *)(qbuf->addr);	/* 缓存中是通信句柄结构体 */
	memset(pscp, 0, sizeof(scp_t) + priv_len);
	pscp->qbuf = qbuf;	/* 将自己通信结构体所在的缓存在此留一个指针入口 */

	/* 初始化所有用户组的队列 */
	for (i = 0; i < SECLVL_COUNT; i++) {
		init_list_head(&pscp->wpkt_list[i]);
	}

	irq_disable(lock);

	/* 用随机值赋值通信连接ID，单次连接拥有统一的ID */
	do {
		pscp->CID = ((rand() & 0xffff) << 16) | (rand() & 0xffff);
	} while (!pscp->CID);

	return pscp;
}

/**
 * \brief	释放已申请的通信句柄
 */
static int scp_delete(scp_t *pscp)
{
	if (!scp_valid(pscp))
		return -EACCES;

	qbuf_free(pscp->qbuf);
	pscp->qbuf = NULL;

	return 0;
}

/**
 * \brief	建立一次通信的前期准备
 * \details	申请用于通信的句柄；需事先申请好通信设备句柄
 * \param	pdev:	设备句柄结构体地址
 */
static scp_handle scp_start(scp_dev_t *pdev)
{
	int res;
	scp_t *pscp;	/* 通信句柄结构体 */

	if (!pdev)
		return NULL;

	pscp = scp_new(0);	/* 申请通信结构体空间 */
	if (!pscp)
		return NULL;

	pscp->pdev = pdev;	/* 已事先分配空间的设备结构体 */

	res = scpdevs_start(pdev);	/* 准备好设备，清空设备缓存 */
	if (res) {
		scp_delete(pscp);	/* 释放已申请的通信结构体 */
		return NULL;
	}

	return (scp_handle)pscp;
}

/**
 * \brief	通信句柄中是否有设备句柄
 * \return	返回设备句柄或者NULL
 */
static scp_dev_t *scp_valid_device(scp_handle hdl)
{
	scp_t *pscp = (scp_t *)hdl;

	if (!scp_valid(pscp))
		return NULL;

	return pscp->pdev;
}

/**
 * \brief	将所有要发送的数据段从发送队列的链表中移除
 * \details	此时数据段所分配的空间仍未释放
 */
static int scp_cancel_all_packets(scp_t *pscp)
{
	int i;
	segment_t *seg = NULL;
	irq_flag lock;

	if (!scp_valid(pscp))
		return -EACCES;

	irq_disable(lock);

	for (i = 0; i < SECLVL_COUNT; i++) {
		/* 用户组循环 */
		while (!list_empty_careful(&pscp->wpkt_list[i])) {
			/* 一系列（多发多收）发送消息的循环，将所有消息节点从系列中删除，
			   通常只用到了单发单收 */
			seg = list_first_entry(&pscp->wpkt_list[i], segment_t, node);
			list_del_init(&(seg->node));
		}
	}

	irq_enable(lock);

	return 0;
}

/**
 * \brief	释放通信句柄
 */
static int scp_destory(scp_t *pscp)
{
	if (!scp_valid(pscp))
		return -EACCES;

	qbuf_free(pscp->qbuf);
	pscp->qbuf = NULL;

	return 0;
}

/**
 * \brief	退出一次通信连接
 */
static int scp_stop(scp_handle hdl)
{
	scp_dev_t *pdev;
	scp_t *pscp = (scp_t *)hdl;

	if (!scp_valid(pscp))
		return -EACCES;

	/* 取消所有（单发单收或多发多收）发送包节点 */
	scp_cancel_all_packets(pscp);

	pdev = (scp_dev_t *)(pscp->pdev);
	if (pdev) {
		scpdevs_exit(pdev);
	}

	scp_destory(pscp);	/* 释放通信句柄 */

	return 0;
}

/********************************** 接口函数 **********************************/
/**
 * \brief	通信协议结构体指针是否有效
 * \return	有效返回真，无效返回假
 */
bool scp_valid(scp_t *pscp)
{
	if (!pscp || !QBUF_ISVALID(pscp->qbuf))
		return false;

	return true;
}

/**
 * \brief	通信协议句柄是否有效
 * \return	有效返回真，无效返回假
 */
bool scp_handle_valid(scp_handle hdl)
{
	scp_t *pscp = (scp_t *)hdl;

	if (!scp_valid(pscp) || !pscp->pdev)
		return false;

	return true;
}

/**
 * \brief	建立一次通信连接
 * \details	可以调用多次，用不同的设备建立各自的连接
 * \param	hwaddr:	如果是进程间通信或多核通信，可以使用内存地址直接传递数据指针，
 *					省掉数据拷贝的时间，提高实时性
 * \param	type:	指定通信设备，如串口、网络、核间通信、进程间通信等，SCP_DEVTYPE_SOCKET...
 */
scp_handle scp_init(uint32_t hwaddr, uint8_t type)
{
	scp_dev_t *pdev;
	scp_handle handle = NULL;

	/* 初始化通信设备 */
	pdev = (scp_dev_t *)scpdevs_init(hwaddr, type);
	if (!pdev)
		return NULL;

	/* 准备好用于通信的空间 */
	handle = scp_start(pdev);
	if (!handle) {
		scpdevs_exit(pdev);	/* 释放设备缓存 */
	}

	return handle;
}

/**
 * \brief	退出当前通信连接
 */
int scp_exit(scp_handle handle)
{
	int res = -EACCES;

	scp_dev_t *pdev;

	if (handle) {
		scp_stop(handle);
	}

	pdev = scp_valid_device(handle);
	if (pdev) {
		res = scpdevs_exit(pdev);
	}

	return res;
}

/*********************************** 文件尾 ***********************************/
